library interfaz;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

{%TogetherDiagram 'ModelSupport_interfaz\default.txaPackage'}
{%TogetherDiagram 'ModelSupport_interfaz\interfaz\default.txaPackage'}
{%TogetherDiagram 'ModelSupport_interfaz\default.txvpck'}
{%TogetherDiagram 'ModelSupport_interfaz\interfaz\default.txvpck'}

uses
  Windows,
  SysUtils,
  AdPort,
  OoMisc,
  AdSelCom,
  ExtCtrls,
  IntList,
  Contnrs,
  Math,
  Dialogs,
  Classes;

{$R *.res}

type
  TInterfaz = Class;

  TValue = Class(TObject)
    Timestamp: TDateTime;  // RETRIEVE IN DD, MM, YY, HH, II, SS, MS
    Tick: LongWord;    // DOUBLE WORD
    Value: Word;
    constructor Create(V: word);
  End;

  TSensor = Class(TComponent)
    private
    Interfaz: TInterfaz;
    DataFile: string;
    f: TextFile;
    published
    Burst: TObjectList;
    Values: TObjectList;
    Timer: TTimer;
    constructor Create;
    procedure SetTimer(Interval: Word);
    procedure ClearTimer;
    procedure OnTimer(Sender: TObject);
    procedure SaveToFile;
    function GetSensor: Word;
    End;

  TInterfaz = Class(TComponent)
    private
    Com : TApdComPort;
    Buffer: TIntList;
    Sensores: array[0..7] of TSensor;
    curSensor: Word;
    BufferOut: TIntList;
    TimerOut: TTimer;
    procedure OnPortTimer(Sender: TObject);
    published
    constructor Create(Port:integer);
    procedure OnTriggerAvail(CP: TObject; Count: Word);
    procedure Close;
    function GetBufferFirst: word;
    procedure Call;
    procedure TalkMotor(Motors: byte);
    procedure MotorOn;
    procedure MotorOff;
    procedure MotorInverse;
    procedure MotorThisWay;
    procedure MotorThatWay;
    procedure MotorCoast;
    procedure MotorPower(Power: byte);
    procedure AllMotorsOff;
    procedure TalkPAP(PAP: Byte);
    procedure PAPSpeed(Speed: Byte);
    procedure PAPSteps(Steps: Word);
    procedure ServoPos(Pos: Byte);
    procedure TalkSensor(Sensor: Byte);
    function GetSensor: Word;
    procedure SensorBurst(Sensors: Byte; Slow: Byte);
    procedure StopBurst;
    function GetBurstValue: Word;
//    function GetBurstTimeStamp(Sensor: Byte): TDateTime;
    function GetBurstTick: LongWord;
    function BurstCount: LongWord;
    function NextBurst: Word;
    procedure ClearBurst;
    function GetSensorValue: LongWord;
    function GetSensorTick: LongWord;
    function NextSensorValue: Word;
    procedure ClearSensorValues;
    function SensorValuesCount: LongWord;
    procedure SetTimer(Interval: Word);
    procedure ClearTimer;
    procedure SaveFile;
    procedure CloseFile;
    procedure SaveBurstToFile;
  End;

var
  Interfaces: array[0..7] of TInterfaz;
  curInterfaz : integer;
  maxInterfaz : integer;
  BaudRate: integer;

constructor TValue.Create(V:word);
begin
  Timestamp := Now();
  Tick := GetTickCount();
  Value := V;
end;

constructor TSensor.Create;
begin
  Burst := TObjectList.Create;
  Values := TObjectList.Create;
  Timer := TTimer.Create(Self);
end;

function TSensor.GetSensor: Word;
var
  Block: TIntList;
  ET : EventTimer;
  ok: Boolean;
begin
  ok := false;
  Block := TIntList.CreateEx;
  NewTimer(ET,16);  // OPTIMIZAR EL TIMER (16 = 1 seg)
  Interfaz.Com.OnTriggerAvail := nil;
  Interfaz.Call;
  Interfaz.Com.PutChar(AnsiChar($20 + (Interfaz.curSensor shl 2)));
  // SACO EL DATO DEL PUERTO
  while (not TimerExpired(ET)) and (not ok) do
  begin
    Interfaz.Com.ProcessCommunications;
    while Interfaz.Com.CharReady do
    begin
      Block.Add(Ord(Interfaz.Com.GetChar));  // SON 4 Bytes: 0x55 0xFF SSH SSL
      ok := true;
    end;
  end;
  Interfaz.Com.OnTriggerAvail := Interfaz.OnTriggerAvail;
  // PROCESAR EL BLOQUE y DEVOLVER EL VALOR
  Result := $400;
  if ok and (Block.IntCount >= 4) then
  begin
    while Block.IntCount > 4 do Block.Delete(0);
    if (Block.Integers[0]=$55) and (Block.Integers[1]=$FF) and (Block.Integers[2]<>$AA) then
      Result := Block.Integers[2] * 256 + Block.Integers[3];
  end;
end;

procedure TSensor.OnTimer(Sender: TObject);
var
  value : TValue;
  fdata: string;
begin
    value := TValue.Create(GetSensor);
    Values.Add(value);
    if DataFile <> '' then
    begin
      fdata := FormatDateTime('dd/mm/yyyy hh:nn:ss:z',value.Timestamp)
        +';'+IntToStr(value.Tick)+';'+IntToStr(value.Value);
     WriteLn(f,fdata);
    end;
end;


procedure TSensor.SetTimer(Interval: Word);
begin
    Timer.Interval := Interval;
    Timer.OnTimer := OnTimer;
    Timer.Enabled := true;
end;

procedure TSensor.ClearTimer;
begin
  Timer.Enabled := false;
end;

procedure TSensor.SaveToFile;
var
  s: TSaveDialog;
  i: integer;
  value: TValue;
  fdata: string;
begin
  s := TSaveDialog.Create(Self);
  s.Options := [ofOverwritePrompt,ofPathMustExist];
  s.DefaultExt := 'csv';
  s.Filter := 'Todos los archivos|*.*';
  if s.Execute then
  begin
    AssignFile(f,s.FileName);
    Rewrite(f);
    fdata := 'DATE;TICK;VALUE';
    WriteLn(f,fdata);
    while Burst.Count > 0 do
    begin
      value := (Burst[0] as TValue);
      fdata := FormatDateTime('dd/mm/yyyy hh:nn:ss:z',value.Timestamp)
        +';'+IntToStr(value.Tick)+';'+IntToStr(value.Value);
      WriteLn(f,fdata);
      Burst.Delete(0);
    end;
    CloseFile(f);
  end;
end;

procedure TInterfaz.OnTriggerAvail(CP: TObject; Count: Word);
var
   i,dataHigh,sensor: integer;
   data: Byte;
   value : TValue;
   fdata: string;
   burstHigh, burstLow: boolean;
begin
  // ANALIZE INPUT
   burstHigh := false;
   burstLow := false;
   sensor := 0;
   for i:=0 to Count -1 do
   begin
//        Application.ProcessMessages;
        data := Ord(Com.GetChar);
        if burstLow then
        begin
             value := TValue.Create(dataHigh * 256 + data);
             Sensores[sensor].Burst.Add(value);
             if Sensores[sensor].DataFile <> '' then
             begin
                fdata := FormatDateTime('dd/mm/yyyy hh:nn:ss:z',value.Timestamp)
                  +';'+IntToStr(value.Tick)+';'+IntToStr(value.Value);
               WriteLn(Sensores[sensor].f,fdata);
             end;
             burstLow := false;
        end;
        if burstHigh then
        begin
             dataHigh := data and 3;
             sensor := data shr 5;
             burstHigh := false;
             burstLow := true;
        end;
        if data = $0C then burstHigh := true;
        // ANY OTHER OPERATION, CODE IT HERE
  // LOG EVERY INPUT IN BUFFER
    if Buffer.IntCount >= 65000 then Buffer.ClearEx(65000);
    Buffer.Add(data);
   end;
end;

constructor TInterfaz.Create(Port:integer);
var
  i: byte;
begin
    Com := TApdComPort.Create(Self);
    Buffer := TIntList.CreateEx;
    BufferOut := TIntList.CreateEx;
    TimerOut := TTimer.Create(Self);
    TimerOut.Interval := 1;
    TimerOut.OnTimer := OnPortTimer;
    TimerOut.Enabled := true;
    for i := 0 to 7 do
    begin
        Sensores[i] := TSensor.Create;
        Sensores[i].Interfaz := Self;
    end;
    Com.ComNumber := Port;
    Com.Baud := BaudRate;
    Com.OnTriggerAvail := OnTriggerAvail;
    Com.Open := true;
end;

procedure TInterfaz.OnPortTimer(Sender: TObject);
begin
   if BufferOut.IntCount > 0 then
   begin
      Com.PutChar(AnsiChar(BufferOut.Integers[0]));
      BufferOut.Delete(0);
   end;
end;

function TInterfaz.GetBufferFirst: word;
begin
  if Buffer.IntCount > 0 then
  begin
    Result := Buffer.Integers[0];
    Buffer.Delete(0);
  end else Result := 0;
end;

procedure TInterfaz.Close;
begin
  Com.Open := false;
  Free;
end;

procedure TInterfaz.Call;
begin
  BufferOut.Add(($54));
  BufferOut.Add(($FE));
end;

procedure TInterfaz.MotorOn;
begin
  Call;
  BufferOut.Add(($40));
end;

procedure TInterfaz.MotorOff;
begin
  Call;
  BufferOut.Add(($44));
end;

procedure TInterfaz.MotorInverse;
begin
  Call;
  BufferOut.Add(($48));
end;

procedure TInterfaz.MotorThisWay;
begin
  Call;
  BufferOut.Add(($4C));
end;

procedure TInterfaz.MotorThatWay;
begin
  Call;
  BufferOut.Add(($50));
end;

procedure TInterfaz.MotorCoast;
begin
  Call;
  BufferOut.Add(($54));
end;

procedure TInterfaz.MotorPower(Power:byte);
begin
  Call;
  BufferOut.Add(($60));
  BufferOut.Add((Power));
end;

procedure TInterfaz.TalkMotor(Motors: Byte);
begin
  Call;
  BufferOut.Add(($80));
  BufferOut.Add((Motors));  // One bit per Motor
end;

procedure TInterfaz.TalkPAP(PAP: byte);
begin
  Call;
  BufferOut.Add(($80));
  BufferOut.Add(($10 + $10 * PAP));  // PAP: 0 or 1
end;

procedure TInterfaz.PAPSpeed(Speed: byte);
begin
  Call;
  BufferOut.Add(($64));
  BufferOut.Add((Speed));
end;

procedure TInterfaz.PAPSteps(Steps: Word);
begin
  Call;
  BufferOut.Add(($C0 + Steps shr 8));
  BufferOut.Add((Steps and $FF));
end;

procedure TInterfaz.AllMotorsOff;
begin
  Call;
  BufferOut.Add(($70));
  BufferOut.Add(($00));
end;

procedure TInterfaz.ServoPos(Pos: Byte);
begin
  Call;
  BufferOut.Add(($68));
  BufferOut.Add((Pos));
end;

procedure TInterfaz.TalkSensor(Sensor: Byte);
begin
  curSensor := Sensor;
end;

function TInterfaz.GetSensor: Word;
begin
  Result := Sensores[curSensor].GetSensor;
end;


function TInterfaz.GetSensorValue: LongWord;
begin
  if Sensores[curSensor].Values.Count > 0 then
    Result := (Sensores[curSensor].Values.Items[0] as TValue).Value
  else Result := $400;

end;

function TInterfaz.GetSensorTick: LongWord;
begin
  if Sensores[curSensor].Values.Count > 0 then
    Result := (Sensores[curSensor].Values.Items[0] as TValue).Tick;
end;

procedure TInterfaz.ClearSensorValues;
begin
  Sensores[curSensor].Values.Clear;
end;

function TInterfaz.NextSensorValue: Word;
begin
  if Sensores[curSensor].Values.Count > 0 then
  begin
      Sensores[curSensor].Values.Delete(0);
      Result := 1;
  end else Result := 0;
end;


procedure TInterfaz.SensorBurst(Sensors: Byte; Slow: Byte);
var
  i: integer;
  j: byte;
begin
  j := 1;
  for i := 0 to 7 do
  begin
    if (Sensors and j) > 0 then Sensores[i].Burst.Clear;
    j := j shl 1;
  end;
  Call;
  if Slow = 1 then BufferOut.Add(($A1))
  else BufferOut.Add(($A0));
  BufferOut.Add((Sensors));
end;

procedure TInterfaz.StopBurst;
begin
  Call;
  BufferOut.Add(($A0));
  BufferOut.Add(($00));
end;

function TInterfaz.BurstCount: LongWord;
begin
  if Sensores[curSensor].Burst.Count > 0 then
    Result := Sensores[curSensor].Burst.Count
  else Result := 0;
end;

function TInterfaz.GetBurstValue: Word;
begin
  if Sensores[curSensor].Burst.Count > 0 then
  begin
    Result := (Sensores[curSensor].Burst.First as TValue).Value;
  end
  else Result := $400;
end;

function TInterfaz.GetBurstTick: LongWord;
begin
  if Sensores[curSensor].Burst.Count > 0 then
    Result := (Sensores[curSensor].Burst.Items[0] as TValue).Tick
  else Result := 0;
end;

function TInterfaz.NextBurst: Word;
begin
  if Sensores[curSensor].Burst.Count > 0 then
  begin
    Sensores[curSensor].Burst.Delete(0);
    if Sensores[curSensor].Burst.Count > 0 then
      Result := 1
    else Result := 0;
  end else
    Result := 0;
end;

procedure TInterfaz.ClearBurst;
begin
  Sensores[curSensor].Burst.Clear;
end;

function TInterfaz.SensorValuesCount: LongWord;
begin
  Result := Sensores[curSensor].Values.Count;
end;

procedure TInterfaz.SetTimer(Interval: Word);
begin
  Sensores[curSensor].SetTimer(Interval);
end;

procedure TInterfaz.ClearTimer;
begin
  Sensores[curSensor].ClearTimer;
end;

procedure TInterfaz.SaveFile;
var
  s: TSaveDialog;
begin
  s := TSaveDialog.Create(Self);
  s.Options := [ofOverwritePrompt,ofPathMustExist];
  s.DefaultExt := 'csv';
  s.Filter := 'Todos los archivos|*.*';
  if s.Execute then
  begin
    if Sensores[curSensor].DataFile <> '' then
      CloseFile;
    Sensores[curSensor].DataFile := s.FileName;
    AssignFile(Sensores[curSensor].f,s.FileName);
    Rewrite(Sensores[curSensor].f);
  end;
end;

procedure TInterfaz.CloseFile;
begin
  System.CloseFile(Sensores[curSensor].f);
  Sensores[curSensor].DataFile := '';
end;

procedure TInterfaz.SaveBurstToFile;
begin
  Sensores[curSensor].SaveToFile;
end;

{********* MODELO DE PROCS PARA EXPORTAR ************}
function Test:word; stdcall;
begin
  Result := 1;
end;
{****************************************************}

function NewInterfaz(Port:integer):word; stdcall;
var
  s: TInterfaz;
begin
  if IsPortAvailable(Port) then
  begin
    s := TInterfaz.Create(Port);
    Interfaces[maxInterfaz] := s;
    curInterfaz := maxInterfaz;
    maxInterfaz := maxInterfaz + 1;
    if Interfaces[curInterfaz].Com.Open then
      Result := curInterfaz
    else Result := High(Result);
  end else
    Result := High(Result);
end;

function GetBuffer: word; stdcall;
begin
  Result := Interfaces[curInterfaz].GetBufferFirst;
end;

procedure CloseInterfaz; stdcall;
begin
    Interfaces[curInterfaz].Close;
end;

function GetBufferCount: word; stdcall;
begin
  Result := Interfaces[curInterfaz].Buffer.IntCount;
end;


procedure TalkInterfaz(num: integer); stdcall;
begin
  curInterfaz := num;
end;

procedure TalkMotor(Motors: Byte); stdcall;
begin
  Interfaces[curInterfaz].TalkMotor(Motors);
end;

procedure MotorOn; stdcall;
begin
  Interfaces[curInterfaz].MotorOn;
end;

procedure MotorOff; stdcall;
begin
  Interfaces[curInterfaz].MotorOff;
end;

procedure MotorInverse; stdcall;
begin
  Interfaces[curInterfaz].MotorInverse;
end;

procedure MotorThisWay; stdcall;
begin
  Interfaces[curInterfaz].MotorThisWay;
end;

procedure MotorThatWay; stdcall;
begin
  Interfaces[curInterfaz].MotorThatWay;
end;

procedure MotorCoast; stdcall;
begin
  Interfaces[curInterfaz].MotorCoast;
end;

procedure MotorPower(Power: byte); stdcall;
begin
  Interfaces[curInterfaz].MotorPower(Power);
end;

procedure TalkPAP(PAP: byte); stdcall;
begin
  Interfaces[curInterfaz].TalkPAP(PAP);
end;

procedure PAPSPeed(Speed: byte); stdcall;
begin
  Interfaces[curInterfaz].PAPSpeed(Speed);
end;

procedure PAPSteps(Steps: byte); stdcall;
begin
  Interfaces[curInterfaz].PAPSteps(Steps);
end;

procedure TalkSensor(Sensor: Byte); stdcall;
begin
  Interfaces[curInterfaz].TalkSensor(Sensor);
end;

function GetSensor:Word; stdcall;
begin
  Result := Interfaces[curInterfaz].GetSensor;
end;

procedure SensorBurst(Sensors: Byte; Slow: Byte); stdcall;
begin
  Interfaces[curInterfaz].SensorBurst(Sensors,Slow);
end;

procedure StopBurst; stdcall;
begin
  Interfaces[curInterfaz].StopBurst;
end;

function GetBurstValue:Word; stdcall;
begin
  Result := Interfaces[curInterfaz].GetBurstValue;
end;

function GetBurstTick:LongWord; stdcall;
begin
  Result := Interfaces[curInterfaz].GetBurstTick;
end;

function BurstCount:LongWord; stdcall;
begin
  Result := Interfaces[curInterfaz].BurstCount;
end;

function NextBurst: Word; stdcall;
begin
  Result := Interfaces[curInterfaz].NextBurst;
end;

procedure ClearBurst; stdcall;
begin
  Interfaces[curInterfaz].ClearBurst;
end;

function GetSensorValue: LongWord; stdcall;
begin
  Result := Interfaces[curInterfaz].GetSensorValue;
end;

function GetSensorTick: LongWord; stdcall;
begin
  Result := Interfaces[curInterfaz].GetSensorTick;
end;

function NextSensorValue: Word; stdcall;
begin
  Result := Interfaces[curInterfaz].NextSensorValue;
end;

procedure ClearSensorValues; stdcall;
begin
  Interfaces[curInterfaz].ClearSensorValues;
end;

function SensorValuesCount: LongWord; stdcall;
begin
  Result := Interfaces[curInterfaz].SensorValuesCount;
end;

procedure SetTimer(Interval: Word); stdcall;
begin
  Interfaces[curInterfaz].SetTimer(Interval);
end;

procedure ClearTimer; stdcall;
begin
  Interfaces[curInterfaz].ClearTimer;
end;

procedure SaveFile; stdcall;
begin
  Interfaces[curInterfaz].SaveFile;
end;

procedure CloseFile; stdcall;
begin
  Interfaces[curInterfaz].CloseFile;
end;

procedure SaveBurstToFile; stdcall;
begin
  Interfaces[curInterfaz].SaveBurstToFile;
end;

procedure SetBaudRate(BR: integer); stdcall;
begin
  BaudRate :=  BR;
end;

exports Test, NewInterfaz, CloseInterfaz, GetBuffer, GetBufferCount,
        TalkInterfaz, MotorOn, MotorOff, MotorInverse,
        TalkMotor, MotorThisWay, MotorThatWay, MotorCoast,
        MotorPower, TalkPAP, PAPSpeed, PAPSteps, TalkSensor,
        GetSensor, SensorBurst, StopBurst, GetBurstValue, GetBurstTick, BurstCount,
        NextBurst, ClearBurst, GetSensorValue, GetSensorTick, NextSensorValue,
        ClearSensorValues, SensorValuesCount, SetTimer, ClearTimer,
        SaveFile, CloseFile, SaveBurstToFile, SetBaudRate; // ..,etc,etc;

begin
  maxInterfaz:= 0;
  curInterfaz:= 0;
  BaudRate := 9600;
end.
